﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;

namespace XbdSoft
{
    public class DownloadManager
    {
        public List<IDownload> Downloads { get; set; }
    }

    public class DownloadChangedEventArgs : EventArgs
    {
        public DownloadChangedEventArgs(long totalLength, long downloadlength)
        {
            TotalLength = totalLength;
            DownloadLength = Math.Min(downloadlength, totalLength);
        }

        public long TotalLength { get; protected set; }
        public long DownloadLength { get; protected set; }
    }

    public interface IDownload
    {
        string SaveFilePath { get; set; }
        long TotalLength { get; }
        long DownloadLength { get; }
        string Status { get; }
        float Speed { get; }

        string DownloadUrl { get; }
        void Download();

        event EventHandler<DownloadChangedEventArgs> DownloadChanged;
    }
     
    public class WebClientDownload : IDownload
    {
        #region Ctor

        public WebClientDownload(string downloadUrl)
            :this(downloadUrl,true)
        {  
        }

        public WebClientDownload(string downloadUrl, bool ftpRedirect)
        {
            DownloadUrl = downloadUrl;
            BufferSize = 1024;
            FtpRedirect = ftpRedirect;
            DownloadLength = 0;
            Status = "等待就绪";
            Speed = 0F;
        }
        #endregion

        #region Properties

        public string SaveFilePath { get; set; }
        public string DownloadUrl { get; protected set; }
        public event EventHandler<DownloadChangedEventArgs> DownloadChanged;

        public bool FtpRedirect { get; protected set; }

        public int BufferSize { get; set; }

        private long totalLength = -1;
        public long TotalLength
        {
            get
            {
                if (totalLength < 0)
                {
                    totalLength = GetSize();
                }
                return totalLength;
            }
        }


        public long DownloadLength { get; protected set; }
        public string Status { get; protected set; }

        private string location = null;
        private string Location
        {
            get
            {
                if (String.IsNullOrEmpty(location))
                {
                    location = GetLocation();
                }
                return location;
            }
        }

        public float Speed { get; protected set; }

        #endregion

        #region IDownload Members

        private string GetLocation()
        {
            if (FtpRedirect)
            {
                var request = WebRequest.Create(DownloadUrl) as HttpWebRequest;
                request.AllowAutoRedirect = false;
                var response = request.GetResponse();
                return response.Headers["Location"];
            }
            return DownloadUrl;
        }

        private long GetSize()
        { 
            FtpWebRequest webRequest = FtpWebRequest.Create(Location) as FtpWebRequest;
            webRequest.Method = WebRequestMethods.Ftp.GetFileSize;
            var webResponse = webRequest.GetResponse(); 
            return webResponse.ContentLength;
        }
        

        public void Download()
        { 
            using (WebClient webClient = new WebClient())
            {               
                webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(WebClient_OpenReadCompleted);
                webClient.OpenReadAsync(new Uri(Location));
            }
            Status = "正在下载";
        }         

        void WebClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                try
                {
                    var eventArgs = new DownloadChangedEventArgs(TotalLength, 0);
                    OnDownloadChanged(eventArgs);
                    System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
                    using (var streamReader = new BinaryReader(e.Result))
                    using (var writer = new FileStream(SaveFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite))
                    {
                        var buffer = new byte[BufferSize];
                        DownloadLength = 0;
                        var length = 0;
                        bool hasStop = false;
                        long lastLength = 0;

                        watch.Start();
                        while ((length = streamReader.Read(buffer, 0, BufferSize)) > 0)
                        {
                            if (watch.ElapsedMilliseconds >= 1000)
                            {
                                watch.Stop();
                                lastLength += length;
                                hasStop = true;
                                Speed = lastLength * 1000F / (1024 * watch.ElapsedMilliseconds); 
                            }
                            DownloadLength += length;
                            lastLength += length;

                            writer.Write(buffer, 0, length);
                            writer.Flush();
                            eventArgs = new DownloadChangedEventArgs(TotalLength, DownloadLength);
                            OnDownloadChanged(eventArgs);
                            if (hasStop)
                            {
                                hasStop = false;
                                lastLength = 0;
                                watch.Reset();
                                watch.Restart();
                            }
                        }
                    }
                    Status = "下载完成";
                    Speed = 0;
                    eventArgs = new DownloadChangedEventArgs(TotalLength, DownloadLength);
                    OnDownloadChanged(eventArgs);
                }
                catch
                { }
            }
        }

        #endregion

        #region Virtual Methods

        protected virtual void OnDownloadChanged(DownloadChangedEventArgs e)
        {
            if (DownloadChanged != null)
            {
                DownloadChanged(this, e);
            }
        }

        #endregion

    }
}
